package com.tengju.user.infrastructure.repository;

import com.tengju.user.domain.model.sentiment.Sentiment;
import com.tengju.user.domain.model.sentiment.SentimentId;
import com.tengju.user.domain.model.sentiment.SentimentRepository;
import com.tengju.user.domain.model.staff.StaffSentimentCount;
import com.tengju.user.domain.model.staff.StaffSentimentRecord;
import com.tengju.user.domain.model.user.SsoUserId;
import com.tengju.user.domain.model.user.UserIdCode;
import com.tengju.user.domain.shared.Row;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.StaffSentimentCountDO;
import com.tengju.user.infrastructure.persistence.mybatis.dataobject.UserSentimentDO;
import com.tengju.user.infrastructure.persistence.mybatis.mapper.dealer.custom.UserSentimentDOMapper;
import com.tengju.user.infrastructure.repository.convert.UserSentimentConvert;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Repository
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class SentimentRepositoryImpl implements SentimentRepository {

    private final UserSentimentDOMapper userSentimentDOMapper;


    @Override
    public List<Sentiment> getBySsoUserIds(LocalDateTime createTimeFrom, LocalDateTime createTimeTo, List<SsoUserId> ssoUserIds) {
        if (CollectionUtils.isEmpty(ssoUserIds)) {
            return new ArrayList<>();
        }
        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        List<UserSentimentDO> userSentimentDOList = userSentimentDOMapper.selectBySsoUserIds(createTimeFrom, createTimeTo, collect);
        return userSentimentDOList.stream()
                .map(UserSentimentConvert::convertToModel)
                .collect(Collectors.toList());
    }

    @Override
    public List<Sentiment> getByIdCodes(List<UserIdCode> userIdCodeList) {
        List<Long> idCodes = userIdCodeList.stream().map(UserIdCode::getValue).collect(Collectors.toList());
        List<UserSentimentDO> userSentimentDOList = userSentimentDOMapper.selectByIdCodes(idCodes);
        return userSentimentDOList.stream()
                .map(UserSentimentConvert::convertToModel)
                .collect(Collectors.toList());
    }

    @Override
    public int getStaffSentimentCountTotal(List<LocalDate> localDateBetween,
                                           List<SsoUserId> ssoUserIds,
                                           LocalDateTime startDayMin,
                                           LocalDateTime nextDayMin,
                                           boolean noRecord) {
        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        String virtualTable = buildVirtualTable(localDateBetween, ssoUserIds);
        return userSentimentDOMapper.getStaffSentimentCountTotal(collect, startDayMin, nextDayMin, virtualTable, noRecord);
    }

    @Override
    public List<StaffSentimentCount> getStaffSentimentCount(List<LocalDate> localDateBetween,
                                                            List<SsoUserId> ssoUserIds,
                                                            LocalDateTime startDayMin,
                                                            LocalDateTime nextDayMin,
                                                            boolean noRecord,
                                                            int offset,
                                                            int limit) {
        if (CollectionUtils.isEmpty(ssoUserIds)){
            return Collections.emptyList();
        }
        String virtualTable = buildVirtualTable(localDateBetween, ssoUserIds);
        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        List<StaffSentimentCountDO> staffSentimentCountDOList = userSentimentDOMapper.getStaffSentimentCount(collect,
                startDayMin, nextDayMin, virtualTable, noRecord, offset, limit);
        return UserSentimentConvert.convertToModelCountList(staffSentimentCountDOList);
    }

    @Override
    public List<StaffSentimentCount> getStaffSentimentCountPageList(List<LocalDate> localDateBetween,
                                                                    List<SsoUserId> ssoUserIds,
                                                                    LocalDateTime startDayMin,
                                                                    LocalDateTime nextDayMin,
                                                                    boolean noRecord,
                                                                    int offset,
                                                                    int limit) {

        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        String virtualTable = buildVirtualTable(localDateBetween, ssoUserIds);
        List<StaffSentimentCountDO> staffSentimentCount = userSentimentDOMapper.getStaffSentimentCount(collect, startDayMin, nextDayMin, virtualTable, noRecord, offset, limit);
        return UserSentimentConvert.convertToModelCountList(staffSentimentCount);
    }

    @Override
    public List<StaffSentimentRecord> getStaffSentimentRecordPageList(List<SsoUserId> ssoUserIds,
                                                                      LocalDateTime startDayMin,
                                                                      LocalDateTime nextDayMin,
                                                                      UserIdCode serviceProviderId,
                                                                      int offset, int limit) {
        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        Long serviceProviderIdValue = Optional.ofNullable(serviceProviderId).map(UserIdCode::getValue).orElse(null);
        List<UserSentimentDO> list = userSentimentDOMapper.getStaffSentimentRecordListByPage(collect, startDayMin, nextDayMin,
                serviceProviderIdValue, offset, limit);
        return UserSentimentConvert.convertToModelRecordList(list);
    }

    @Override
    public int getStaffSentimentRecordTotal(List<SsoUserId> ssoUserIds,
                                            LocalDateTime startDayMin,
                                            LocalDateTime nextDayMin,
                                            UserIdCode serviceProviderId) {
        List<Long> collect = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        Long serviceProviderIdValue = Optional.ofNullable(serviceProviderId).map(UserIdCode::getValue).orElse(null);
        return userSentimentDOMapper.getStaffSentimentRecordTotal(collect, startDayMin, nextDayMin, serviceProviderIdValue);
    }

    private String buildVirtualTable(List<LocalDate> dayIntBetween, List<SsoUserId> ssoUserIds) {
        StringBuilder sb = new StringBuilder("(");
        int i = 0;
        for (LocalDate dayInt : dayIntBetween) {
            for (SsoUserId ssoUserId : ssoUserIds) {
                if (i == 0) {
                    sb.append("select '").append(dayInt).append("' as day , ").append(ssoUserId.getValue()).append(" as sso_user_id from dual");
                } else {
                    sb.append(" union select '").append(dayInt).append("', ").append(ssoUserId.getValue());
                }
                i++;
            }
        }
        sb.append(")");
        return sb.toString();
    }

    private String buildVirtualTable(List<LocalDate> dayIntBetween) {
        StringBuilder sb = new StringBuilder("(");
        int i = 0;
        for (LocalDate dayInt : dayIntBetween) {
            if (i == 0) {
                sb.append("select '").append(dayInt).append("' as day from dual");
            } else {
                sb.append(" union select '").append(dayInt).append("'");
            }
            i++;
        }
        sb.append(")");
        return sb.toString();
    }

    @Override
    public Boolean addSentimentRecord(Sentiment sentiment) {
        if (sentiment == null) {
            return false;
        }
        return userSentimentDOMapper.addSentimentRecord(UserSentimentConvert.convertToDO(sentiment)) == 1;
    }

    @Override
    public Boolean deleteSentimentRecordById(SentimentId sentimentId) {
        if (sentimentId == null) {
            return false;
        }
        return userSentimentDOMapper.deleteSentimentRecord(sentimentId.getValue()) == 1;
    }

    @Override
    public Sentiment getSentimentRecordById(SentimentId sentimentId) {
        if (sentimentId == null) {
            return null;
        }
        UserSentimentDO userSentimentDO = userSentimentDOMapper.getSentimentRecordBySentimentId(sentimentId.getValue());
        return UserSentimentConvert.convertToModel(userSentimentDO);
    }

    @Override
    public Boolean updateSentimentRecord(SentimentId sentimentId, String sentimentContext, SsoUserId ssoUserId, String operator) {
        if (sentimentId == null || ssoUserId == null) {
            return false;
        }
        return userSentimentDOMapper.updateSentimentRecord(sentimentId.getValue(), sentimentContext, ssoUserId.getValue(), operator) == 1;
    }



    @Override
    public List<Row> monthSentimentRecordChartAnalysis(List<SsoUserId> ssoUserIds, List<LocalDate> localDateBetween, LocalDateTime startMonthMin, LocalDateTime nextMonthMin) {
        String virtualTable = buildVirtualTable(localDateBetween);
        List<Long> ssoUserIdValueList = ssoUserIds.stream().map(SsoUserId::getValue).collect(Collectors.toList());
        List<Map<String, Object>> result = userSentimentDOMapper.getMonthSentimentRecordChart(ssoUserIdValueList, startMonthMin, nextMonthMin, virtualTable);
        if (CollectionUtils.isEmpty(result)) {
            return Collections.emptyList();
        }
        return result.stream()
                .map(Row::new)
                .collect(Collectors.toList());
    }
}
